home *** CD-ROM | disk | FTP | other *** search
- ;* SNOW.ASM
- ;************************************************************************
- ;* *
- ;* PC Scheme/Geneva 4.00 Borland TASM code *
- ;* *
- ;* (c) 1985-1988 by Texas Instruments, Inc. See COPYRIGHT.TXT *
- ;* (c) 1992 by L. Bartholdi & M. Vuilleumier, University of Geneva *
- ;* *
- ;*----------------------------------------------------------------------*
- ;* *
- ;* Fractal snowflakes for a 386 VGA IBM PC *
- ;* *
- ;*----------------------------------------------------------------------*
- ;* *
- ;* Created by: Larry Bartholdi Date: 1991 *
- ;* Revision history: *
- ;* - 18 Jun 92: Renaissance (Borland Compilers, ...) *
- ;* *
- ;* ``In nomine omnipotentii dei'' *
- ;************************************************************************
- IDEAL
- %PAGESIZE 66, 132
- %TITLE "Fractal snowflakes in assemler"
-
- ; use: (snowflake #size #x #y #angle #level #first)
- ; all arguments are numbers, but nothing is checked for.
- ; typically this code should be interfaced through a nice Scheme
- ; layer that would check the arguments, center the figure,
- ; scale it, etc., all stuff best done in a high level language.
- ;
- ; a good demo example is (snowflake 400 200 10 1 6 1)
-
- ;PROGRAMMER'S NOTE:
- ;
- ; numbers are represented in two formats:
- ; word (16_bit) to represent integer quantities
- ; dword (32_bit) to represent fixed-point quantities
- ; you will see many 'shl eax, 10' in the code. these represent
- ; conversions from one notation to the other.
-
- ;*****************************************************************************
- ;* assembler directives
- ;*****************************************************************************
- P386
- INCLUDE "inline.ash"
-
- ;*****************************************************************************
- ;* a few useful constants
- ;*****************************************************************************
- XSIZE = 640
- YSIZE = 480
- ANGLES = 12
-
- VGA_LATCH = 03dah
- VGA_COLOR = 03c8h
- VGA_PALETTE = 03c0h
- VGA_PAGE = 03cdh
- VGA_MODE = 03ceh
- VGA_MASK = 03c4h
- VGA_SEG = 0a000h
-
- ;*****************************************************************************
- ;* system macros
- ;*****************************************************************************
- MACRO setmode mode ;; set video mode
- mov ax, mode
- int 10h
- ENDM
-
- MACRO getchar ;; get char from keyboard in AX
- mov ah, 0
- int 16h
- ENDM
-
- startinline SNOWFLAKE, 6
- jmp main
-
- ;*****************************************************************************
- ;* data definitions
- ;*****************************************************************************
- len dd ?
- x dd ?
- y dd ?
- angle dw ?
- level dw ?
-
- ; sines and cosines of multiples of 2\pi / ANGLES
-
- sin dd 0h, 8000h, 0ddb4h ; the two tables are interleaved
- cos dd 10000h, 0ddb4h, 8000h ; for maximum space saving
- dd 0h, -8000h, -0ddb4h
- dd -10000h,-0ddb4h,-8000h ; end of sine
- dd 0h, 8000h, 0ddb4h ; end of cosine
-
- label palette BYTE
- db 04h, 0ah, 10h, 15h
- db 1ah, 1fh, 23h, 27h
- db 2bh, 2fh, 32h, 35h
- db 38h, 3bh, 3dh, 3fh
-
- ;*****************************************************************************
- ;* entry point
- ;*****************************************************************************
- PROC main FAR
- ; main procedure
- ; sets up everything 4 the recursion
- ; note in particular that the code is fully relocatable,
- ; that is, all accesses to memory are done relative to IP
- ; thus the followin code:
-
- ; call: (snowflake length x y angle level flag)
-
- call @@next
- @@next:
- pop bp
- sub bp, OFFSET @@next
- ; substract the disk-image address, so
- ; we now have the difference between
- ; a .COM image and what is actually loaded
- ; !! FROM NOW ON, ALL ADDRESSING SHOULD BE DONE RELATIVE TO DS:BP !!
-
- movzx eax, [reg1.disp]; get the passed length
- shl eax, 10h
- mov [cs:bp+len], eax
- movzx eax, [reg2.disp]; get the passed x0
- shl eax, 10h
- mov [cs:bp+x], eax
- movzx eax, [reg3.disp]; get the passed y0
- shl eax, 10h
- mov [cs:bp+y], eax
- mov ax, [reg4.disp] ; get the passed angle
- mov [cs:bp+angle], ax
- mov ax, [reg5.disp] ; get the passed level
- mov [cs:bp+level], ax
-
- cmp [reg6.disp], 0 ; is flag on ?
- je @@skip
- setmode 12h ; VGA 640x480
- call setpalette
- @@skip:
- REPT 3
- call recurse ; draw a kinked line
- call turnright ; and turn right 120^o
- call turnright
- ENDM
- retf
- ENDP main
-
- ;*****************************************************************************
- ;* the real stuff
- ;*****************************************************************************
- PROC recurse
- ; draw a kinked line from (x,y), direction len*cis(angle)
- ; if level=0 draw ____ (total length len)
- ; else draw _/\_ (each segment's length len/3)
- ; assumes: x, y, angle, level contain appropriate data
- ; returns: nothing
- ; sideff: none
- ; trash: ax, bx, dx
- ; tested: LB, 6-XII-91 (Passed)
-
- cmp [cs:bp+level], 1
- je line ; fall through line (tail-recursive!)
-
- push [cs:bp+len] ; save 'em, 'cause we'll directly modify 'em
- ; for the recursion; beware of roundoffs !
-
- dec [cs:bp+level]
- mov eax, [cs:bp+len]
- cdq
- mov ebx, 3
- div ebx
- mov [cs:bp+len], eax
-
- call recurse ; recursively draw the line
- call turnleft
- call recurse
- call turnright
- call turnright
- call recurse
- call turnleft
- call recurse
-
- pop [cs:bp+len]
- inc [cs:bp+level]
- ret
- ENDP recurse
-
- PROC line
- ; draw a straight line from (x,y), direction len*cis(angle)
- ; using a simple anti-aliasing technique
- ; to improve resolution
- ; assumes: x, y, angle contain appropriate data
- ; makes usage of sine and cosine tables
- ; returns: nothing
- ; sideff: updates x and y
- ; trash: eax, ebx, ecx, edx, esi, edi
- ; tested: LB, 6-XII-91 (Passed)
-
- mov di, [cs:bp+angle] ; get angle
- shl di, 2 ; make it a dword index
- mov ecx, [cs:bp+len]
- shr ecx, 0ah ; we now have fixed-point, with a 6-bits
- ; fractional part (to avoid precision losses)
- adc ecx, 0
- mov edx, ecx
- imul ecx, [cs:bp+cos+di] ; ecx = \Delta_x
- imul edx, [cs:bp+sin+di] ; edx = \Delta_y
-
- sar ecx, 6 ; shift out the remaining 6 bits
- adc ecx, 0 ; round up
- sar edx, 6
- adc edx, 0 ; and we again have legal (16-bit fraction)
- ; fixed-point numbers
-
- movzx ebp, bp ; assume we have dx > dy.
- ; we use the 16 high bits of ebp
- ; to store the orientation
- mov esi, [cs:bp+x] ; get (x,y) in (esi,edi)
- mov edi, [cs:bp+y]
- add [cs:bp+x], ecx ; and update the coordinates
- add [cs:bp+y], edx
- mov eax, ecx
- or eax, eax
- jg @@xpositive
- neg eax ; eax is abs(\Delta_x)
- @@xpositive:
- mov ebx, edx
- or ebx, ebx
- jg @@ypositive
- neg ebx ; ebx is abs(\Delta_y)
- @@ypositive:
- ; we now have (esi,edi) = starting point
- ; (ecx,edx) = \Delta
- ; (eax,ebx) = abs(\Delta)
-
- cmp eax, ebx ; is dx > dy ?
- ja @@goodslope
- xchg esi, edi ; if not, swap all _x and _y
- xchg eax, ebx ; so the slope is -1 <= ... <= 1
- xchg ecx, edx
- add ebp, 10000h ; and notify you did it, so the plot
- ; routine can unswap x and y
- @@goodslope:
- or ecx, ecx ; is the 'x' increment positive ?
- jg @@left2right
- add esi, ecx ; else scan from right to left
- neg ecx ; negate the increment
- add edi, edx
- neg edx ; and change the other coordinate
- @@left2right:
- ; we may now loop from esi to esi+ecx
-
- xor eax, eax
- shrd eax, edx, 10h
- sar edx, 10h ; eax:edx (64_bit) is set to 10000 * dy
- idiv ecx ; now eax contains 10000 * dy/dx, that is,
- ; y_increment
- mov edx, eax ; put it in edx
- shr ecx, 10h ; make ecx a loop counter
- ; by keeping the integer part of \Delta_x
- inc cx ; and round up
- @@loop:
- call plot ; plot at (esi,edi)
- add esi, 10000h
- add edi, edx
- loop @@loop
- ret ; whew ! we did it !
- ENDP line
-
- PROC plot
- ; plot point at (esi,edi)
- ; where these 32-bit registers should be shifted left by 16
- ; to yield pixel coordinates. This actually is fixed-point
- ; arithmetic, necessary to plot at non-integer pixel coordinates
- ; (that is the idea of anti-aliasing).
- ;
- ; assumes: esi = x/y-coor, edi = y/x-coor, orientation
- ; returns: nothing
- ; sideff: writes in video memory
- ; trash: none
- ; tested: LB, 16-XII-91
-
- pushad ; save the stuff
-
- mov ebx, ebp
- shr ebx, 10h ; get high word of ebp = orientation
- ; in bx
-
- shr esi, 10h
- adc si, 0 ; now si is integer 'x' coordinate
- mov eax, edi
- shr edi, 10h ; edi is integer 'y' coordinate
- ; note: DON'T round up !
- shr eax, 0ch
- and ax, 0fh ; ax [0-f] is fractional part.
- ; put color ax @ (si,di+1)
- ; and color !ax @ (si,di)
- inc di ; first put next
- or bx, bx ; did we swap x & y ?
- jz @@xy1 ; nope
- xchg si, di ; yup. unswap
- @@xy1:
- call putpoint ; and put a point
- or bx, bx ; did we swap x & y ?
- jz @@xy2 ; nope
- dec si ; previous x
- inc di ; will be undone by next instruction
- @@xy2:
- dec di ; previous y
- not ax
- and ax, 0fh ; complement the color
- call putpoint ; put the other point
- popad ; restore the stuff
- ret
- ENDP plot
-
- PROC putpoint
- ; adds the color in al to the point at (si,di)
- ; if the sum of both colors exceeds 0f, leave 0f
- ;
- ; assumes: si = x_cor, di = y_cor, al = color
- ; returns: nothing
- ; sideff: writes directly in the VGA memory; writes directly
- ; to the video adapter's ports
- ; trash: none
- ; tested: LB, 12-XII-91
-
- push es ; save the stuff
- pushad
- push ax ; save the color
-
- mov ax, VGA_SEG ; set segment to VGA ram
- mov es, ax
-
- mov eax, XSIZE shr 3 ; BYTEs per row
- mul edi ; times Y
- movzx ebx, si
- shr ebx, 3
- add eax, ebx ; plus X / 8
- mov di, ax ; save it as a pointer
- shr eax, 10h ; get page number
- and al, 0fh ; mask is useless...
- mov ah, al
- shl ah, 4
- or al, ah ; transfer to both nibbles
- mov dx, VGA_PAGE
- out dx, al ; write to page register
-
- ; now get the current color
-
- mov cx, si
- and cx, 7 ; keep 3 bits
- mov bx, 0080h
- shr bx, cl ; now bx is the mask
- push bx ; save the mask. it will be useful
- ; later.
- neg cl
- add cl, 7-3 ; complement the mask, substract 3
- and cl, 7 ; again keep 3 bits
- mov ax, 0304h ; loop 3+1 times, request = 4
- mov dx, VGA_MODE
- @@loop:
- out dx, ax ; set to color plane ah
- mov ch, [BYTE es:di] ; get the color
- and ch, bl ; keep one bit
- ror ch, cl ; ror by cl, so we don't lose
- ; anything though carrying out
- or bh, ch ; and set this bit in result
- inc cl ; get next bit
- dec ah ; in previous plane
- jns @@loop ; loop
-
- ; we now have bh = color
- ; write it back, adding the requested color
-
- pop ax ; pop back the mask in al
- pop cx ; and the color in cl
- add bh, cl ; add both colors
- cmp bh, 10h ; 2 big ?
- jb @@ok ; nope
- mov bh, 0fh ; yup. back to 0f (maximum value)
- @@ok:
- mov ah, al ; move the mask to ah
- mov al, 08h ; request = 8
- mov dx, VGA_MODE
- out dx, ax ; set mode to write, pixel X & 7
-
- mov ax, 3
- out dx, ax ; ???
- mov dx, VGA_MASK
- mov ax, 0f02h
- out dx, ax ; write mask = ALL, mode 2
- mov ah, [BYTE es:di] ; set latch registers
- mov [BYTE es:di], 0 ; clear the pixel
- mov ah, bh ; get color
- out dx, ax ; write the color
- mov ah, [BYTE es:di] ; set latch
- mov [BYTE es:di], 0ffh ; and set these pixels
- mov ah, 0fh
- out dx, ax ; put back a standard color
- mov dx, VGA_MODE
- mov ax, 0ff08h
- out dx, ax ; put back standard mode and pixel
-
- popad ; restore regs
- pop es
- ret ; home sweet home
- ENDP putpoint
-
- PROC setpalette
- ; sets the graphical palette to grey tones
- ; so we can plot using anti-aliasing
- ;
- ; assumes: palette and paltable
- ; returns: nothing
- ; sideff: directly writes to the VGA card
- ; trash: ax, cx, dx, si
- ; caveat: clears interrupts
- ; tested: LB, 21-VIII-92
-
- cli
-
- mov dx, VGA_LATCH ; magic ?
- in al, dx
-
- mov dx, VGA_COLOR
- mov al, 0 ; starting at color 0
- out dx, al
- inc dx
-
- lea si, [bp+palette]
- mov cx, 10h ; 10h colors in mode 640x480
- @@loop1:
- lods [BYTE cs:si]
- out dx, al ; output the r,g,b (all the same)
- out dx, al
- out dx, al
- loop @@loop1
-
- mov dx, VGA_PALETTE
- mov al, 0
- mov cx, 10h
- @@loop2:
- out dx, al
- out dx, al
- inc al
- loop @@loop2
- mov al, 11h ; set overscan color
- out dx, al
- mov al, 0 ; to 0
- out dx, al
- mov al, 20h ; notify end of palette
- out dx, al
-
- sti
- ret
- ENDP setpalette
-
- PROC turnright
- ; moves the pointer's direction \pi/3 to the right
- ; note: angles are represented as multiples of \pi/3,
- ; in the CW (i.e., unofficial) manner. This is because
- ; the sine tables, ... are defined for CCW orientations,
- ; but the display is top-down reversed !
-
- mov ax, [cs:bp+angle]
- add ax, ANGLES/6 ; turn right 2\pi / 6
- cmp ax, ANGLES
- jb @@cont
- sub ax, ANGLES
- @@cont:
- mov [cs:bp+angle], ax
- ret
- ENDP turnright
-
- PROC turnleft
- mov ax, [cs:bp+angle]
- sub ax, ANGLES/6
- jnb @@cont
- add ax, ANGLES
- @@cont:
- mov [cs:bp+angle], ax
- ret
- ENDP turnleft
-
- endinline
- END